/*  PLUTO JS -Scripting Web Server.
    Copyright (C) 2013 vivek gangadharan

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package pluto.http;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpCookie;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.logging.Level;
import static pluto.core.AppLogger.*;
import pluto.core.Localizer;
import pluto.core.Message;

public class HTTPResponse {

    private final OutputStream os;
    private HashMap<String, String> headers = new HashMap<>();
    private StringBuilder buffer = new StringBuilder();
    private String responseCode = HTTPDefinition.HTTPStatus.OK;
    private String reponseType = HTTPDefinition.MIMEType.MIME_PLAINTEXT;
    private List<HttpCookie> cookies = new ArrayList();
    private String redirect;
    private String sendFile;
    private ReadableByteChannel inputChannel;
    private static java.text.SimpleDateFormat gmtFrmt;
    private boolean isFileDispatch= false;

    public boolean isIsFileDispatch() {
        return isFileDispatch;
    }

    public void setIsFileDispatch(boolean isFileDispatch) {
        this.isFileDispatch = isFileDispatch;
    }

    static {
        gmtFrmt = new java.text.SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
        gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
    }

    public HTTPResponse(OutputStream os) {
        this.os = os;
    }

    public void setInputChannel(ReadableByteChannel channel) {
        inputChannel = channel;
    }

    public void clean() {
    }

    public String sendFile() {
        return sendFile;
    }

    //TODO redirect
    public void sendResponse() throws Exception {
        try {

            if (inputChannel == null) {
                if (buffer.length() > 0) {
                    InputStream inputStream = new ByteArrayInputStream(buffer.toString().getBytes());
                    inputChannel = Channels.newChannel(inputStream);
                }
            }
            PrintWriter pw = new PrintWriter(os);
            pw.print("HTTP/1.0 " + responseCode + " \r\n");

            if (reponseType != null) {
                pw.print("Content-Type: " + reponseType + "\r\n");
            }

            if (headers == null || headers.containsKey("Date")) {
                pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n");
            }

            if (headers != null) {
                Set<Entry<String, String>> entries = headers.entrySet();
                for (Entry<String, String> entry : entries) {
                    pw.print(entry.getKey() + ": " + entry.getValue() + "\r\n");
                }
            }

            if (cookies != null) {
                for (HttpCookie c : cookies) {
                    pw.print("Set-Cookie: " + c.toString());
                }
            }

            pw.print("\r\n");
            pw.flush();
            if (inputChannel != null) {
                WritableByteChannel outputChannel = Channels.newChannel(os);
                fastChannelCopy(inputChannel, outputChannel);
                inputChannel.close();
                outputChannel.close();
            } else {
                os.close();
            }
        } catch (Exception ex) {
            log.log(Level.SEVERE, Localizer.getMessage(Message.ErrorSendingResponse),ex);
                throw ex;
        }
    }

    public void fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException {
        final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
        while (src.read(buffer) != -1) {
            buffer.flip();
            dest.write(buffer);
            buffer.compact();
        }
        buffer.flip();
        while (buffer.hasRemaining()) {
            dest.write(buffer);
        }
    }

    public void header(String key, String value) {
        if (key != null && value != null) {
            headers.put(key, value);
        }
    }

    public String header(String key) {

        return headers.get(key);
    }

    public void removeHeader(String key) {
        if (key != null) {
            headers.remove(key);
        }
    }

    public void send(String message) {
        if (message != null) {
            buffer.append(message);
        }
    }

    public void type(String type) {
        if (type != null) {
            this.reponseType = type;
        }
    }

    public String type() {
        return this.reponseType;
    }

    public void response(String responseCode) {
        this.responseCode = responseCode;
    }

    public String response() {
        return this.responseCode;
    }

    public OutputStream outputStream() {
        return this.os;
    }

    public void cookie(HttpCookie cookie) {
        if (cookie != null) {
            cookies.add(cookie);
        }
    }

    public void redirect(String redirectUrl) {
        if (redirectUrl != null) {
            this.redirect = redirectUrl;
        }
    }

    public void sendFile(String filePath) {
        if (filePath != null) {
            this.sendFile = filePath;
        }
    }
}
